package cn.wolfcode.luowowo.search.service.impl;

import cn.wolfcode.luowowo.search.query.SearchQueryObject;
import cn.wolfcode.luowowo.search.service.ISearchService;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements ISearchService {

    @Autowired
    private ElasticsearchTemplate template;


    //全文搜索 + 高亮显示
    //查询原始的数据
    //将需要高亮显示字段替换原先字段

    // 以攻略查询为例:
    //searchService.searchWithHighlight(StrategyTemplate.INDEX_NAME,
    //        StrategyTemplate.TYPE_NAME, StrategyTemplate.class, qo, "title", "subTitle", "summary");

    //select  * from strategy_detail where title like '%广州%' or summary like '%广州%'
    @Override
    public <T> Page<T> searchWithHighlight(String index, String type, Class<T> clz, SearchQueryObject qo, String... fields) {


        //建立查询语句: GET /luowowo_strategy/strategy
        NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
        searchQuery.withIndices(index)
                .withTypes(type);

        //拼接全文查询语句:
        /*"query":{
            "multi_match": {
                "query": "广州",
                 "fields": ["title","subTitle","summary"]
            }
        },*/
        //查询条件
        searchQuery.withQuery(QueryBuilders.multiMatchQuery(qo.getKeyword(), fields));

        //分页
        /*"from": 0,
        "size":3,*/
        searchQuery.withPageable(qo.getPageable());


        //设置高亮显示内容

       /* "highlight": {
            "fields" : {
                "title" : {},
                "subTitle" : {},
                "summary" : {}
            }
        }
       */
        String preTags = "<span style='color:red;'>";
        String postTags = "</span>";

        //需要进行高亮显示的字段数组
        //而每一个Field 对象表示一个 需要高亮显示的字段
        HighlightBuilder.Field[] fs = new HighlightBuilder.Field[fields.length];
        for(int i = 0; i < fs.length; i++){

            //参数: fields : "title", "subTitle", "summary"
            fs[i] = new HighlightBuilder.Field(fields[i])
                    .preTags(preTags)
                    .postTags(postTags);
        }
        searchQuery.withHighlightFields(fs);


        //参数1:查询对象: 查询语句的封装对象
        //参数2:对象类型, 查询结果返回Page对象中数据类型: Page<T> 这个T, 本例子中StrategyTemplate.class
        //参数3:查询结果处理映射对象
        return template.queryForPage(searchQuery.build(), clz, new SearchResultMapper() {

            //参数1:查询结果对象
            //参数2:对象类型, 查询结果返回Page对象中数据类型: Page<T> 这个T, 本例子中StrategyTemplate.class
            //参数3:分页对象
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                List<T> list = new ArrayList<>();
                SearchHits hits = response.getHits();
                SearchHit[] searchHits = hits.getHits();
                //遍历查询结果封装成对象: 本例子 StrategyTemplate对象
                for (SearchHit searchHit : searchHits) {

                    //对象封装: 本例子 StrategyTemplate对象
                    //返回仅仅原始的数据, 没有高亮显示的功能
                    T t = JSON.parseObject(searchHit.getSourceAsString(), clazz);


                    //进行高亮显示, 使用高亮样式替换
                    highlightFieldsCopy(t, searchHit.getHighlightFields(), fields);
                    list.add(t);
                }


                AggregatedPage<T> result = new AggregatedPageImpl<T>((List<T>) list, pageable,
                        response.getHits().getTotalHits());
                return result;
            }
        });
    }


    //参数1:原始的对象, 此时属性没有替换
    //参数2:高亮显示字段对象, 是一个map, key:高亮显示字段名称, value:高亮显示内容, 带上标签
    //参数3:需要高亮显示字段
    private <T> void highlightFieldsCopy(T t, Map<String, HighlightField> map, String ...fields){

        //一个临时map,
        //key:需要高亮替换的字段: 本例子, tilte, subTitle , summary
        //value: 具有高亮显示样式的文本值
        Map<String, String> mm = new HashMap<>();

        for (String field : fields) {

            HighlightField hf = map.get(field);
            if (hf != null) {

                Text[] fragments = hf.fragments();
                String str = ""; //具有高亮显示样式的文本值
                for (Text text : fragments) {
                    str += text;
                }
                mm.put(field, str); //title: "<em>广州</em>小吃名店红黑榜：你还是当年珠江畔那个老字号吗？"  subTitle:"<em>广州</em>小吃名店红黑榜"
            }
        }
        try {
            BeanUtils.copyProperties(t, mm);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}
